#include <cassert>
#include <iostream>
#include <sstream>
using namespace std;

template<typename T=double>
class Vec3 {
public:
  // 默认构造函数：自动生成的版本不会初始化基础类型的成员，所以要人工定义。
  Vec3() : x(), y(), z() {} // 成员全初始化为0。
  Vec3(T tx, T ty, T tz) : x(tx), y(ty), z(tz) {}
  // 数据成员全是基础类型，自动生成的析构、拷贝构造、拷贝赋值算符就合用。

  // 重载下标算符。注意同名、同参数、仅常性不同的成员函数也可以组成重载。
  T  operator[](int i) const { return (&x)[i]; } // 返回值，常性
  T& operator[](int i)       { return (&x)[i]; } // 返回引用，可以修改成员。

  // 点乘算符（dot product），因为结果是T类型，和this不同，点乘不适用自反赋值形式。
  T operator*(const Vec3<T>& v) const {
    return x * v.x + y * v.y + z * v.z;
  }

  Vec3<T> operator^(const Vec3<T>& v) const { // 叉乘算符（cross product）
    return Vec3{
      y * v.z - z * v.y, // 排布成易于比较的形式。源码换行不会影响运行效率。
      z * v.x - x * v.z,
      x * v.y - y * v.x
    };
  }

  // 叉乘结果也是向量，可以重载自反赋值形式的算符。
  // 同类中Vec3<T>可以简写为Vec3，请与上例对比。
  Vec3& operator^=(const Vec3& v) { // 需要向this赋值，就不能是常性函数。
    return *this = (*this ^ v);     // 借用上面的叉乘算符、拷贝赋值实现功能。
  }

private:
  T x, y, z; // 因为数据成员设计为私有，可以改成T a[3]而对外部毫无影响。
};

// 输入、输出算符的首个参数是istream, ostream，属于标准库，我们不能修改。
// 属于典型的只能用自由函数来重载的算符，难以作为成员函数重载。

template<typename T>
istream& operator>>(istream& ist, Vec3<T>& v) { // 重载输入算符。v需要修改，就不能是常量。
  return ist >> v[0] >> v[1] >> v[2]; // 用了返回引用的下标算符
}

template<typename T>
ostream& operator<<(ostream& ost, const Vec3<T>& v) { // 重载输入算符
  return ost << "(Vec3 " << v[0] << ' ' << v[1] << ' ' << v[2] << ")"; // 常性下标算符
}

int main() {
  Vec3<double> v0;      // 默认构造
  Vec3<> v1{1, 2, 3};   // 分量构造
  cout << v0 << endl;   // 输出算符
  cout << v1 << endl;

  istringstream iss("4 5.0 +6.00"); // 用字符串输入流测试
  iss >> v1;                    // 输入
  v0 = v1;                      // 拷贝赋值
  cout << v0 << endl;
  cout << (v0 * v0) << endl;    // 点乘

  Vec3<double> vx{1, 0, 0}, vy{0, 1, 0};
  cout << (vx ^  vy) << endl;   // 叉乘
  cout << (vx ^= vy) << endl;   // 自反赋值形式的叉乘
}